<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<script>
const obj = {
    name: "jrd",
    sayName: function(a, b) {
        console.log(this.name, a, b);
    }
}
const obj2 = {
    name: "hhh",
}
obj.sayName.apply(obj2); // hhh
// 手写apply方法思路：首先我们把myApply挂载到Function.prototype上以便所有的函数都可以访问
// 通过this就可以访问到调用myApply的函数（函数体），记为x
// 我们的核心就是修改x的this，修改为myApply的第一个参数context，所以我们的思路就是把函数体挂载到context上，然后context[XXX]()自然就达到了目标
// 为了防止覆盖context上原有的属性，我们创建一个Symbol对象作为独一无二的键值用来挂载函数体，最后delete context[XXX]删除这个属性就好了
// 至于apply和call方法的核心上面就实现了，只是再提案加一下传参就好了，对于这里的apply方法，参数为一个数组，我们只需要拓展运算符处理一下传给函数体（调用myApply的函数体）即可
Function.prototype.myApply = function(context, arr) {
    const fun = this;
    const tempProp = Symbol();
    context[tempProp] = fun;
    const result = context[tempProp](...arr);
    delete context[tempProp];
    return result;
}
obj.sayName.myApply(obj2, [1, 2, 3]);
// bind的话目标就是返回一个永久改变this为context的函数，复用一下apply的代码很容易就可以实现
Function.prototype.myBind = function(context, ...arr) {
    const fun = this;
    return function(...args) {
        return fun.myApply(context, [...arr, ...args]);
    }
}
const bindSayName = obj.sayName.myBind(obj2, 1);
bindSayName(2);
</script>
</body>
</html>